package gcontent.config;

import gcontent.config.error.ConfigEx;
import gcontent.config.node.GlobalNode;
import gcontent.config.node.content.ContentNode;
import gcontent.config.node.filter.FilterNode;
import gcontent.config.node.filter.FilterRefNode;
import gcontent.config.node.record.RecordNode;
import gcontent.config.node.site.SiteNode;
import gcontent.config.persistents.node.ObjectNode;
import gcontent.entities.Content;
import gcontent.entities.content.ContentManager;
import gcontent.filter.Filter;
import gcontent.filter.FilterChain;
import gcontent.filter.error.FilterEx;
import gcontent.init.Init;
import gcontent.record.Record;
import gcontent.record.error.RecordEx;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Configure {
	
	public static final String GCONTENT_CONF = "/gcontent.config";

	/* global properties defined */
	public static Properties prop = new Properties();
	
	/* filters loaded */
	private static HashMap<String, FilterNode> filter_node = new HashMap<String, FilterNode>();
	
	/* recorders loaded */
	private static HashMap<String, RecordNode> record_node = new HashMap<String, RecordNode>();
	
	/* site */
	private static SiteNode site_node = null;

	/* content */
	private static HashMap<String, ContentNode> content_node = new HashMap<String, ContentNode>();
	
	/* content type filters */ 
	private static HashMap<String, Vector<Filter>> content_filters = new HashMap<String, Vector<Filter>>();
	
	/* site filters */
	private static HashMap<String, Vector<Filter>> site_filters = new HashMap<String, Vector<Filter>>();
	
	/* site filters */
	private static HashMap<String, Vector<Filter>> site_trace_filters = new HashMap<String, Vector<Filter>>();
	
	/* default config */
	private static GlobalNode global_node = null;
	
	public static HashMap<String, Vector<Filter>> getTracedSites()
	{
		return site_trace_filters;
	}
		
	public static HashMap<String, Vector<Filter>> getSiteFilters()
	{
		return site_filters;
	}
	
	public static HashMap<String, Vector<Filter>> getContentFilters()
	{
		return content_filters;
	}

	public static void set(String name, String value)
	{
		prop.setProperty(name, value);
	}
	
	public static String get(String name)
	{
		return prop.getProperty(name);
	}
	
	public static int getRecordType(Content content) throws ConfigEx
	{				
		ContentNode content_n;
		
		content_n = content_node.get(content.getFormat());
		if(content_n == null && (content_n = content_node.get(Content.DEF_TYPE_FORMAT)) == null )
			throw new ConfigEx("invalid content type " + content.getFormat());
		
		return content_n.getRecordType();
	}

	public static Record getGlobalRecord() throws ConfigEx, RecordEx
	{				
		if(global_node == null || global_node.getRecordId() == null)
			throw new ConfigEx("not data-record defined!");
				
		return record_node.get(global_node.getRecordId()).getRecord();
	}
	
	public static Record getGlobalRecordTmp() throws ConfigEx, RecordEx
	{				
		if(global_node == null || global_node.getRecordIdTmp() == null)
			throw new ConfigEx("not data-record defined!");
				
		return record_node.get(global_node.getRecordIdTmp()).getRecord();
	}
	
	private static Filter getFilter(FilterRefNode ref, String type, int order) throws ConfigEx, FilterEx
	{
		FilterNode filter_n;
		String filter_name;
		Filter filter;
		
		filter_n = filter_node.get(ref.getId());
		if(filter_n == null)
			throw new ConfigEx("not filter id " + ref.getId() + " exist");
		
		filter_name = filter_n.getName();
		try { 
			filter = (Filter)Configure.class.getClassLoader().loadClass(filter_name).newInstance();
		} catch(Exception ex) { 
			throw new ConfigEx("not class filter found => " + filter_name);
		}
		
		if(type == null)
			filter.setType(FilterChain.DEF_TYPE);
		
		filter.setOrder(order + 1);
		filter.setSuccess(ref.getSuccess());
		filter.setFail(ref.getFail());
		filter.setInput(filter_n.getInput());
		filter.setReversed(ref.getReversed());
		
		return filter;
	}
	

	private static void setFilters(HashMap<String, Vector<Filter>> filters,Vector<FilterRefNode> filter_ref, String type) throws ConfigEx, FilterEx
	{
		for(int o = 0; o < filter_ref.size(); o++) {
			FilterRefNode ref;
			Filter filter;
			
			ref = filter_ref.get(o);				
			filter = getFilter(ref, type, o);
			
			FilterChain.setFilter(filters, filter, filter_ref.size());
		}	
	}
	
	public static void init() throws ConfigEx
	{
		InputStream in;
		
		in = Init.class.getResourceAsStream(GCONTENT_CONF);
		if(in == null)
			throw new ConfigEx("not found resource " + GCONTENT_CONF + "!!");
		
		Configure.load(in);
	}

	public static void load(InputStream in) throws ConfigEx
	{
		DocumentBuilderFactory dbfactory;
		DocumentBuilder builder; 		
		Document doc;

		try {
			NodeList node_list;
			
			dbfactory =  DocumentBuilderFactory.newInstance();
			builder = dbfactory.newDocumentBuilder();
			doc = builder.parse(in);
			
			node_list = doc.getDocumentElement().getChildNodes();
			for(int i = 0; node_list.item(i) != null; i++) {
				Node node;	
				String node_name;
				
				node = node_list.item(i);
				node_name = node.getNodeName();
				if(node_name.equals("filter")) {
					FilterNode filter = new FilterNode(node);
					filter_node.put(filter.getId(), filter);
				} else if(node_name.equals("content")) {
					ContentNode content = new ContentNode(node);
					content_node.put(content.getType(), content);
				} else if(node_name.equals("record")) {
					RecordNode record = new RecordNode(node);
					record_node.put(record.getId(), record);	
				} else if(node_name.equals("site")) {
					site_node = new SiteNode(node);
				} else if(node_name.equals("global")) {	
					global_node = new GlobalNode(node);
				} else if(node_name.charAt(0) != '#')
					throw new ConfigEx("unknown node " + node_name);				 
			}
			
			if(site_node != null) {
				setFilters(site_filters, site_node.getFiltersRef(), null);
				setFilters(site_trace_filters, site_node.getTraceFiltersRef(), null);
			}
			
			Iterator<String> it_cont = content_node.keySet().iterator();
			/* load content filters */
			while(it_cont.hasNext()) {
				ContentNode cont_node;
				String type;
				
				type = it_cont.next();
				cont_node = content_node.get(type);
		
				if(ContentManager.getContent(type) == null)
					throw new ConfigEx("invalid content type " + type + " is not registered");
				
				setFilters(content_filters, cont_node.getFiltersRef(), type);
			}
	
		} catch(Exception ex) {
			throw new ConfigEx(ex);			
		}
	}
	
}
